package cn.ldj.ztomcat.util;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 迷你浏览器
 */
public class MiniBrowser {
    /**
     * MiniBrowser 主要提供如下方法：
     * 1. getHttpBytes 返回二进制的 http 响应
     * 2. getHttpString 返回字符串的 http 响应
     * 3. getContentBytes 返回二进制的 http 响应内容 （可简单理解为去掉头的 html 部分）
     * 4. getContentString 返回字符串的 http 响应内容 （可简单理解为去掉头的 html 部分）
     */
    public static void main(String[] args) throws Exception {
        String url = "http://127.0.0.1:18080";
        System.out.println("获取响应体");
        String contentString = getContentString(url, false);
        System.out.println(contentString);
        System.out.println("获取响应体行头体");
        String httpString = getHttpString(url, false);
        System.out.println(httpString);
    }

    public static byte[] getContentBytes(String url) {
        return getContentBytes(url, false);
    }

    public static String getContentString(String url) {
        return getContentString(url, false);
    }

    public static String getContentString(String url, boolean gzip) {
        byte[] result = getContentBytes(url, gzip);
        if (null == result)
            return null;
        try {
            return new String(result, "utf-8").trim();
        } catch (UnsupportedEncodingException e) {
            return null;
        }
    }

    /**
     * 通过java.net发起HTTP GET请求和获取响应体
     */
    public static byte[] getContentBytes(String url, boolean gzip) {
        //获取到行头体
        /**
         * 响应内容为==
         * HTTP/1.1 200 OK
         * Content-Type: text/html
         *
         * Hello DIY Tomcat from ldj
         */
        byte[] response = getHttpBytes(url, gzip);

        //从行头体中取出响应体
        byte[] doubleReturn = "\r\n\r\n".getBytes();
        //找到头和体之间空行所在的byte位置。从数据第一个位置挨个往后取空行的距离，取出来的内容等于空行，就找到了空行所在的下标位置
        int pos = -1;
        for (int i = 0; i < response.length - doubleReturn.length; i++) {
            byte[] temp = Arrays.copyOfRange(response, i, i + doubleReturn.length);

            if (Arrays.equals(temp, doubleReturn)) {
                pos = i;
                break;
            }
        }
        if (-1 == pos)
            return null;
        //获取空行末尾的下标，就是体的开头在数组中的位置
        pos += doubleReturn.length;

        //上面确定了空行所在的位置，从该位置起，后面的内容就是体，复制到新数组返回
        byte[] result = Arrays.copyOfRange(response, pos, response.length);
        return result;
    }

    public static String getHttpString(String url, boolean gzip) {
        byte[] bytes = getHttpBytes(url, gzip);
        return new String(bytes).trim();
    }

    public static String getHttpString(String url) {
        return getHttpString(url, false);
    }

    /**
     * 通过java.net发起HTTP GET请求和获取响应结果（行头体）
     */
    public static byte[] getHttpBytes(String url, boolean gzip) {
        byte[] result = null;
        try {
            URL u = new URL(url);
            Socket client = new Socket();
            int port = u.getPort();
            if (-1 == port)
                port = 80;
            InetSocketAddress inetSocketAddress = new InetSocketAddress(u.getHost(), port);
            //与服务端Bootstrap开启的socket端口建立socket连接，然后构造HTTP请求报文，通过字符流将报文写到Bootstrap.java
            client.connect(inetSocketAddress, 1000);
            Map<String, String> requestHeaders = new HashMap<>();

            requestHeaders.put("Host", u.getHost() + ":" + port);
            requestHeaders.put("Accept", "text/html");
            requestHeaders.put("Connection", "close");
            requestHeaders.put("User-Agent", "ldj mini brower / java1.8");

            if (gzip)
                requestHeaders.put("Accept-Encoding", "gzip");

            String path = u.getPath();
            if (path.length() == 0)
                path = "/";

            String firstLine = "GET " + path + " HTTP/1.1\r\n";

            StringBuffer httpRequestString = new StringBuffer();
            httpRequestString.append(firstLine);
            Set<String> headers = requestHeaders.keySet();
            for (String header : headers) {
                String headerLine = header + ":" + requestHeaders.get(header) + "\r\n";
                httpRequestString.append(headerLine);
            }

            //通过字符输出流，将构造的HTTP请求报文写到服务端
            PrintWriter pWriter = new PrintWriter(client.getOutputStream(), true);
            pWriter.println(httpRequestString);
            InputStream is = client.getInputStream();

            int buffer_size = 1024;

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte buffer[] = new byte[buffer_size];
            while (true) {
                int length = is.read(buffer);
                if (-1 == length)
                    break;
                baos.write(buffer, 0, length);
                if (length != buffer_size)
                    break;
            }

            result = baos.toByteArray();
            client.close();
        } catch (Exception e) {
            e.printStackTrace();
            try {
                result = e.toString().getBytes("utf-8");
            } catch (UnsupportedEncodingException e1) {
                e1.printStackTrace();
            }
        }

        return result;

    }

    /**
     * 字节缓冲流读取的响应行头体
     */
    public static byte[] readBytes(InputStream is, boolean b) throws IOException {
        int buffer_size = 1024;
        byte buffer[] = new byte[buffer_size];
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        while (true) {
            int length = is.read(buffer);
            if (-1 == length)
                break;
            baos.write(buffer, 0, length);
            if (length != buffer_size)
                break;
        }
        byte[] result = baos.toByteArray();
        return result;
    }
}
